import React, { useState } from "react";
import {
  Button,
  Upload,
  message as antdMessage,
  Progress,
  Row,
  Col,
  Divider,
} from "antd";
import "./style.css";
import { UploadRequestOption } from "rc-upload/lib/interface";
import SparkMD5 from "spark-md5";
import axios from "axios";
import { useTranslation } from "react-i18next";

interface ChunkType {
  name: string;
  chunk: Blob;
  originName: string;
  percent: number;
}

const Index: React.FC = () => {
  const { t } = useTranslation();
  const [chunks, setChunks] = useState<ChunkType[]>([]);
  const [uploadInfo, setUploadInfo] = useState({
    filename: "",
    total: 0,
  });
  // 开始请求--采用自定义上传方式
  const customRequest = async (options: UploadRequestOption) => {
    // 获取上传的文件
    const file = options.file as File;
    // 文件扩展名
    const extname = file.name.slice(file.name.lastIndexOf("."));
    // 获取上传文件的hash
    const hash = await createFileMD5Hash(file);
    // 请求后端文件检查接口
    const checkFileResult = await checkFile(hash);
    // 设置进度
    setUploadInfo({
      filename: file.name,
      total: 0,
    });
    // 获取检查检测信息
    const { code, data } = checkFileResult.data;

    // 根据状态码 判断
    if (code === 200 || code === 202) {
      // 没有上传 || 上传了一部分
      // 展示进度
      // setShowUploadProgress(true)
      // 准备上传的分片
      const chunks = createFileChunks(file, hash, extname);
      // 已经上传的分片
      const existsChunks = data.existsChunks || [];
      // 每一个分片的进度大小占总进度大小
      const intervalProgress = 100 / chunks.length;

      // 当前进度
      let currentProgress = 0;
      // 断点续传 + 当前进度设置
      chunks.forEach((v) => {
        if (existsChunks.includes(v.name)) {
          currentProgress += intervalProgress;
          v.percent = 100;
        }
        // return !existsChunks.includes(v.name)
      });

      // 展示分片
      setChunks(chunks);
      setUploadInfo({
        filename: file.name,
        total: +currentProgress.toFixed(2),
      });
      // 将分片转化 FormData
      const formDataChunk = chunks
        .filter((v) => v.percent !== 100)
        .map((v) => {
          const form = new FormData();
          form.append("file", v.chunk, v.name);
          form.append("name", v.name);
          return form;
        });

      // 发送所有请求 || 处理高并发问题  || 最多同时发送6个分片
      const requestingList: Promise<any>[] = [];

      while (formDataChunk.length) {
        const formData = formDataChunk.shift() as FormData;
        const request = axios.post("/api/upload", formData, {
          onUploadProgress(progressEvent) {
            const percent = (progressEvent.loaded / progressEvent.total) * 100;

            setChunks((state) => {
              const list = [...state];
              const s = list.find((v) => v.name === formData.get("name"));
              if (s) {
                s.percent = +Number.parseInt(percent.toString());
              }

              return list;
            });

            if (progressEvent.loaded === progressEvent.total) {
              setUploadInfo((state) => {
                return {
                  ...state,
                  total: state.total + intervalProgress,
                };
              });
            }
          },
        });
        requestingList.push(request);

				request.then(() => {
					const i = requestingList.findIndex(v => v === request)
					requestingList.splice(i, 1)
				})

        if (requestingList.length >= 6) {
          await Promise.race(requestingList);
        }
      }
      // const requestAll = formDataChunk.map((formData) => {
      // return axios.post("/api/upload", formData, {
      //   onUploadProgress(progressEvent) {
      //     const percent = (progressEvent.loaded / progressEvent.total) * 100;

      //     setChunks((state) => {
      //       const list = [...state];
      //       const s = list.find((v) => v.name === formData.get("name"));
      //       if (s) {
      //         s.percent = +Number.parseInt(percent.toString());
      //       }

      //       return list;
      //     });

      //     if (progressEvent.loaded === progressEvent.total) {
      //       setUploadInfo((state) => {
      //         return {
      //           ...state,
      //           total: state.total + intervalProgress,
      //         };
      //       });
      //     }
      //   },
      // });
      // });

      // 有等待所有请求成功
      await Promise.all(requestingList);
      // // 合并切片
      await axios.post("/api/merge/file", {
        hash,
        extname,
        originName: file.name,
        size: file.size,
        len: 200,
      });

      antdMessage.success("上传成功");
      setUploadInfo({
        ...uploadInfo,
        total: 100,
      });
      options.onSuccess && options.onSuccess(options);
    } else {
      // 已经上传完毕 或者 上传了一部分
      if (code === 201) {
        antdMessage.success("秒传成功");
        setUploadInfo({
          ...uploadInfo,
          total: 100,
        });
        setChunks([]);
        options.onSuccess && options.onSuccess(options);
      }
    }
  };

  // 创建md5 hash file
  const createFileMD5Hash = (file: File): Promise<string> => {
    return new Promise((resolve) => {
      const blobSlice = File.prototype.slice,
        chunkSize = 2097152, // Read in chunks of 2MB
        chunks = Math.ceil(file.size / chunkSize),
        spark = new SparkMD5.ArrayBuffer(),
        fileReader = new FileReader();

      let currentChunk = 0;

      fileReader.onload = function (e) {
        if (
          e.target &&
          e.target.result !== null &&
          typeof e.target.result !== "string"
        )
          spark.append(e.target.result); // Append array buffer
        currentChunk++;

        if (currentChunk < chunks) {
          loadNext();
        } else {
          resolve(spark.end());
        }
      };

      fileReader.onerror = function () {
        console.warn("oops, something went wrong.");
      };

      function loadNext() {
        const start = currentChunk * chunkSize,
          end = start + chunkSize >= file.size ? file.size : start + chunkSize;

        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
      }

      loadNext();
    });
  };

  // 检车文件函数
  const checkFile = (hash: string) => {
    return axios.post("/api/check/file", { hash });
  };

  // 文件分片
  const createFileChunks = (
    file: File,
    hash: string,
    extname: string,
    chunkSize = 2 * 1024 ** 2
  ) => {
    const maxIndex = Math.ceil(file.size / chunkSize);
    const chunks: ChunkType[] = [];
    for (let i = 0; i < maxIndex; i++) {
      const start = i * chunkSize;
      const end = start + chunkSize > file.size ? file.size : start + chunkSize;
      chunks.push({
        name: `${hash}-${i}${extname}`,
        chunk: file.slice(start, end),
        originName: `${file.name.slice(
          0,
          file.name.lastIndexOf(".")
        )}-${i}${extname}`,
        percent: 0,
        // count: 3,
      });
    }
    return chunks;
  };

  return (
    <div>
      <div className="upload">
        <Upload customRequest={(options) => customRequest(options)}>
          <Button type="primary">{t("upload.uploadFile")}</Button>
        </Upload>
      </div>
      {
        <div>
          <div>
            <h2>{t("upload.totalUploadingProgress")}</h2>
            <Row>
              <Col span={4}>{uploadInfo.filename}</Col>
              <Col span={20}>
                <Progress percent={Math.round(uploadInfo.total)} />
              </Col>
              <Divider></Divider>
            </Row>
          </div>
          <div className="upload-list">
            <h2>{t("upload.fragmentList")}</h2>
            {chunks.map((v, i) => {
              return (
                <Row key={i}>
                  <Col span={4}>{v.originName}</Col>
                  <Col span={20}>
                    <Progress percent={Math.round(v.percent)} />
                  </Col>
                  <Divider></Divider>
                </Row>
              );
            })}
          </div>
        </div>
      }
    </div>
  );
};
export default Index;
